#region Using directives

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

using Weazel.Gezel.Model;
using Weazel.Gezel.Parser;
using Weazel.Gezel.Model.Errors;
using Weazel.Gezel.Simulator.Reference.Profiling;

#endregion

namespace Weazel.Gezel.Simulator.Reference.Setup
{
    /// <summary>
    /// The SimulationSetup is the top level object which executes
    /// with the simulation setup phase. Datapaths, controllers and
    /// expressions are setup in dedicated setup classes: DatapathSetup,
    /// ControllerSetup and ExpressionSetup.
    /// </summary>
    public static class SimulationSetup
    {
        public static void SetupFromFile(Simulation sim, string description, string fileName)
        {
            // parse the file
            List<ParseError> parseErrors = new List<ParseError>();
            Weazel.Gezel.Model.Model model = ParseModel.Parse(ref parseErrors, description, fileName);


            // print out any parse errors
            if (parseErrors.Count > 0)
            {
                Console.Error.WriteLine("Simulation was aborted because of these parse error(s):");
                foreach (ParseError error in parseErrors)
                {
                    Console.Error.WriteLine();
                    Console.Error.WriteLine(error.ToString());
                }

                // if the model contained errors
                // don't try to setup simulation
                Environment.Exit(1);
            }

            // verify the file
            List<VerificationError> verificationErrors = null;
            List<VerificationWarning> warnings = null;

            model.Verify(ref verificationErrors, ref warnings);

            // print errors if any
            if (verificationErrors.Count > 0)
            {
                Console.Error.WriteLine("Simulation was aborted because of the these verification error(s):");
                foreach (VerificationError error in verificationErrors)
                {
                    Console.Error.WriteLine();
                    Console.Error.WriteLine(error.ToString());
                }

                // if the model contained errors
                // don't try to setup simulation
                Environment.Exit(1);
            }

            if (warnings.Count > 0)
            {
                Console.Error.WriteLine("The following warning(s) was detected during verification:");
                foreach (VerificationWarning warning in warnings)
                {
                    Console.Error.WriteLine();
                    Console.Error.WriteLine(warning.ToString());
                }
            }

            SetupFromModel(sim, model);
        }

        public static void SetupFromModel(Simulation sim, Weazel.Gezel.Model.Model model)
        {
            //
            // setup options
            //
            foreach (OptionDirective option in model.OptionDirectives)
            {
                if (option.Value == "vcd" && sim.dump == null)
                    sim.dump = new ValueChangeDump("TRACE.vcd");
                else if (option.Value == "profile_toggle_alledge" && sim.profiler == null)
                    sim.profiler = new Profiler(false);
                else if (option.Value == "profile_display_operations" && sim.profiler != null)
                    sim.profiler.DisplayOperations = true;
            }

            //
            // setup constants
            //
            setupConstants(sim, model);

            //
            // setup ipblocks
            //
            IpBlockSetup.setupIpBlocks(sim, model);

            //
            // setup datapaths
            //
            DatapathSetup.setupDatapaths(sim, model);

            //
            // setup controllers
            //
            ControllerSetup.setupController(sim, model);

            //
            // setup systems
            //
            setupSystems(sim, model);
        }

        private static void setupConstants(Simulation sim, Weazel.Gezel.Model.Model model)
        {
            foreach (LeafExpression constant in model.Constants.Values)
            {
                ConstantSignal signal = new ConstantSignal(constant.Id, constant.IntegerValue);
                sim.signals.Add(constant.Id, signal);
            }
        }

        private static void setupSystems(Simulation sim, Weazel.Gezel.Model.Model model)
        {
            foreach (Weazel.Gezel.Model.system system in model.Systems.Values)
            {
                foreach (Weazel.Gezel.Model.Stimulus stimulus in system.Stimuli.Values)
                {
                    FileSignal file = new FileSignal(stimulus.Id, stimulus.Filename, stimulus.Name);
                    sim.signals[stimulus.Id] = file;
                }

                // connect system nets
                foreach (Weazel.Gezel.Model.system.Net net in system.Nets.Values)
                {
                    foreach (InPort ip in net.InPorts)
                    {
                        if (net.Stimulus != null)
                        {
                            //Console.WriteLine("{0} is driven by {1}", ip.Id, net.Stimulus.Id);
                            sim.signals[ip.Id].defineDriver(sim.signals[net.Stimulus.Id]);
                        }
                        else
                        {
                            //Console.WriteLine("{0} is driven by {1}", ip.Id, net.OutPort.Id);
                            sim.signals[ip.Id].defineDriver(sim.signals[net.OutPort.Id]);
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Looks up the component to which an idenfier points and
        /// returns the id of the corresponding component.
        /// </summary>
        /// <param name="datapath">datapath to use as identifier scope</param>
        /// <param name="identifierExpr">the identifier</param>
        /// <returns></returns>
        internal static int getRefId(Weazel.Gezel.Model.Datapath datapath, LeafExpression identifierExpr)
        {
            if (datapath.InPorts.ContainsKey(identifierExpr.Value))
                return datapath.InPorts[identifierExpr.Value].Id;
            else if (datapath.OutPorts.ContainsKey(identifierExpr.Value))
                return datapath.OutPorts[identifierExpr.Value].Id;
            else if (datapath.Registers.ContainsKey(identifierExpr.Value))
                return datapath.Registers[identifierExpr.Value].Id;
            else if (datapath.Signals.ContainsKey(identifierExpr.Value))
                return datapath.Signals[identifierExpr.Value].Id;
            else if (datapath.LookupTables.ContainsKey(identifierExpr.Value))
                return datapath.LookupTables[identifierExpr.Value].Id;
            else if (datapath.Model.Constants.ContainsKey(identifierExpr.Value))
                return datapath.Model.Constants[identifierExpr.Value].Id;
            else
            {
                foreach (SignalFlowGraph sfg in datapath.SignalFlowGraphs.Values)
                {
                    if (sfg.Signal != null && sfg.Signal.LocalName == identifierExpr.Value)
                        return sfg.Signal.Id;
                }

                // ok, identifier did not point to something valid
                // must be an error in the gezel model 
                throw new InvalidDataException();
            }
        }
    }
}